package com.song.demo.webmvc.core;

import com.song.demo.webmvc.annotation.GPAutowired;
import com.song.demo.webmvc.annotation.GPController;
import com.song.demo.webmvc.annotation.GPService;
import com.song.demo.webmvc.aop.*;
import com.song.demo.webmvc.beans.GPBeanDefinition;
import com.song.demo.webmvc.beans.GPBeanWrapper;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

/**
 * IOC DI mvc aop
 */
public class GPApplicationContext extends  DefaultListableBeanFactory implements GPBeanFactory{

    private String[]  configLocations;

    private GPBeanDefinitionReader reader;

    //单例的IOC容器缓存 以类名第一个字母小写为key
    private Map<String,Object>  factoryBeanObjectCache = new ConcurrentHashMap<>();

    //通用的ioc容器 以类名第一个字母小写为key
    private Map<String, GPBeanWrapper> factoryBeanInstanceCache = new ConcurrentHashMap<>();

    public GPApplicationContext(String ... configLocations){
        this.configLocations = configLocations;
        try{
            refresh();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public void refresh() throws Exception {
        //1.定位配置文件
        GPBeanDefinitionReader gpBeanDefinitionReader = new GPBeanDefinitionReader(this.configLocations);
        this.reader = gpBeanDefinitionReader;
        //2.加载配置文件，扫描相关的类，把他们封装成beanDefinition
        List<GPBeanDefinition> beanDefinitions =  reader.loadBeanDefinitions();
        //3.注册，把配置信息放到容器里面
        doRegisterBeanDefinition(beanDefinitions);
        //4.把不是延迟加载的类提前初始化
        doAutowired();
    }

    //只处理非延迟加载的类
    private void doAutowired() {
        for (Map.Entry<String, GPBeanDefinition> entry : super.beanDefinitionMap.entrySet()) {
            String beanName = entry.getKey();
            if (!entry.getValue().isLazyInit()){
                try{
                    getBean(beanName);
                }catch (Exception e){
                    e.printStackTrace();
                }

            }


        }
    }

    private void doRegisterBeanDefinition(List<GPBeanDefinition> beanDefinitions) throws Exception {
        for (GPBeanDefinition beanDefinition : beanDefinitions) {
            if (super.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())) {
                throw  new Exception("The " + beanDefinition.getFactoryBeanName() + "is exists!");
            }
            super.beanDefinitionMap.put(beanDefinition.getFactoryBeanName(),beanDefinition);
            //到这里为止，容易初始化完毕

        }
    }

    /**
     * beanFactory的核心方法
     * @param name
     * @return
     * @throws Exception
     */
    @Override
    public Object getBean(String name) throws Exception {
        GPBeanDefinition beanDefinition = super.beanDefinitionMap.get(name);
        try{
            //生成通知事件
            PGBeanPostProcessor beanPostProcessor = new PGBeanPostProcessor();
            Object instance = instantiateBean(beanDefinition);
            if (instance == null){
                return  null;
            }
            //在实例初始化之前调用一次
            beanPostProcessor.postProcessBeforeInitialization(instance, name);

            GPBeanWrapper gpBeanWrapper = new GPBeanWrapper(instance);

            this.factoryBeanInstanceCache.put(name,gpBeanWrapper);
            //在实例初始化之后调用一次
            beanPostProcessor.postProcessAfterInitialization(instance, name);
            //实例化bean的依赖
            populateBean(name,instance);
            return this.factoryBeanInstanceCache.get(name).getWrappedInstance();


        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

    /**
     *  对Bean属性的依赖注入进行处理
     * @param name
     * @param instance
     */
        private void populateBean(String name, Object instance) {

            Class<?> clazz = instance.getClass();
            //这里只解析controller和service
            if(!(clazz.isAnnotationPresent(GPController.class)|| clazz.isAnnotationPresent(GPService.class))){
                return;
            }
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                if (!field.isAnnotationPresent(GPAutowired.class)){
                    continue;
                }
                GPAutowired annotation = field.getAnnotation(GPAutowired.class);
                String autowiredBeanName = annotation.value().trim();
                if ("".equals(autowiredBeanName)){
                    autowiredBeanName = field.getType().getName();
                }
                field.setAccessible(true);
                try{
                    field.set(instance,this.factoryBeanInstanceCache.get(autowiredBeanName).getWrappedInstance());
                }catch (IllegalAccessException e){
                    e.printStackTrace();
                }
            }
        }

    /**
     *初始化bean
     * 传一个BeanDefinition ,就返回一个实例bean
     * @param beanDefinition
     * @return
     */
    private Object instantiateBean(GPBeanDefinition beanDefinition) {
        Object instance = null;
        //beanClassName为类名小写
        String factoryBeanName = beanDefinition.getFactoryBeanName();
        String beanClassName = beanDefinition.getBeanClassName();
        try{
            //因为根据Class才能确定一个类是否有实例
            if (this.factoryBeanObjectCache.containsKey(factoryBeanName)){
                instance = this.factoryBeanObjectCache.get(factoryBeanName);
            }else{
                Class<?> clazz = Class.forName(beanClassName);
                instance = clazz.newInstance();
                //创建aop代理类的配置信息
                GPAdvisedSupport advisedSupport = instantiationAopConfig(beanDefinition);
                advisedSupport.setTargetClass(clazz);
                advisedSupport.setTarget(instance);
                //是否创建代理类
                if (advisedSupport.pointCutMatch()){
                    instance = createProxy(advisedSupport).getProxy();
                }

                this.factoryBeanObjectCache.put(factoryBeanName,instance);
                return  instance;
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return  instance;
    }
    private GPAopProxy createProxy(GPAdvisedSupport advisedSupport){
        Class targetClass = advisedSupport.getTargetClass();
        if (targetClass.getInterfaces().length>0){
            return  new GPJdkDynamicAopProxy(advisedSupport);
        }
        return  new GPCglibAopProxy(advisedSupport);
    }



    /**
     * 封装aop的配置
     * @param beanDefinition
     * @return
     */
    private GPAdvisedSupport instantiationAopConfig(GPBeanDefinition beanDefinition) {
        GPAopConfig aopConfig = new GPAopConfig();
        Properties config = reader.getConfig();
        aopConfig.setAspectAfter(config.getProperty("aspectAfter"));
        aopConfig.setAspectAfterThrow(config.getProperty("aspectAfterThrow"));
        aopConfig.setAspectAfterThrowingName(config.getProperty("aspectThrowingName"));
        aopConfig.setAspectBefore(config.getProperty("aspectBefore"));
        aopConfig.setAspectClass(config.getProperty("aspectClass"));
        aopConfig.setPointCut(config.getProperty("pointCut"));

        return new GPAdvisedSupport(aopConfig);
    }

    @Override
    public Object getBean(Class<?> clazz) throws Exception {
        return null;
    }

    public Properties getConfig(){
        return  this.reader.getConfig();
    }
    public String[] getBeanDefinitionNames(){
        return this.beanDefinitionMap.keySet().toArray(new String[this.beanDefinitionMap.size()]);
    }

    public int getBeanDefinitionCount(){
        return this.beanDefinitionMap.size();
    }






}
